package gstool

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

// ParamExtractor 参数提取器
// 从json字符串或者对象数组等中提取某个值
type ParamExtractor struct {
	data interface{}
}

// NewJsonExtractorFromJSON 从JSON字符串创建参数提取器
func NewJsonExtractorFromJSON(jsonStr string) (*ParamExtractor, error) {
	var res interface{}
	err := JsonDecode(jsonStr, &res)
	if err != nil {
		return nil, fmt.Errorf("failed to parse JSON: %v", err)
	}

	return &ParamExtractor{
		data: res,
	}, nil
}

// NewJsonExtractorFromAny 从任意值创建参数提取器
func NewJsonExtractorFromAny(res interface{}) *ParamExtractor {
	return &ParamExtractor{
		data: res,
	}
}

// Extract 提取参数，支持点号和数组索引访问，如 "a[0]" 或 "a.b[0].c" 空值表示原数据返回
func (pe *ParamExtractor) Extract(path string) (interface{}, error) {
	if path == "" {
		return pe.data, nil
	}
	// 解析路径
	parts := pe.parsePath(path)
	// 从根开始逐步提取
	current := pe.data
	for _, part := range parts {
		var err error
		current, err = pe.getValue(current, part)
		if err != nil {
			return nil, err
		}
	}
	return current, nil
}

// GetRawData 获取原始数据
func (pe *ParamExtractor) GetRawData() interface{} {
	return pe.data
}

// GetJSONString 获取原始数据的JSON字符串
func (pe *ParamExtractor) GetJSONString() string {
	return JsonEncode(pe.data)
}

// parsePath 解析路径，如 "a[0].b[1].c" -> ["a", "[0]", "b", "[1]", "c"]
func (pe *ParamExtractor) parsePath(path string) []string {
	var parts []string
	var current strings.Builder
	i := 0
	for i < len(path) {
		char := path[i]

		if char == '.' {
			if current.Len() > 0 {
				parts = append(parts, current.String())
				current.Reset()
			}
			i++
		} else if char == '[' {
			if current.Len() > 0 {
				parts = append(parts, current.String())
				current.Reset()
			}
			// 查找对应的右括号
			end := i
			for end < len(path) && path[end] != ']' {
				end++
			}
			if end == len(path) {
				// 没有找到右括号，错误
				parts = append(parts, path[i:])
				break
			}
			parts = append(parts, path[i:end+1])
			i = end + 1
		} else {
			current.WriteByte(char)
			i++
		}
	}
	if current.Len() > 0 {
		parts = append(parts, current.String())
	}
	return parts
}

// getValue 根据部分路径获取值
func (pe *ParamExtractor) getValue(data interface{}, part string) (interface{}, error) {
	// 检查是否是数组索引
	if strings.HasPrefix(part, "[") && strings.HasSuffix(part, "]") {
		indexStr := part[1 : len(part)-1]
		index, err := strconv.Atoi(indexStr)
		if err != nil {
			return nil, fmt.Errorf("invalid array index: %s", indexStr)
		}
		return pe.getArrayValue(data, index)
	}
	// 否则是对象属性
	return pe.getObjectValue(data, part)
}

// getArrayValue 获取数组中的值
func (pe *ParamExtractor) getArrayValue(data interface{}, index int) (interface{}, error) {
	// 将数据转换为反射值
	rv := reflect.ValueOf(data)
	// 检查是否为切片或数组
	switch rv.Kind() {
	case reflect.Slice, reflect.Array:
		if index < 0 || index >= rv.Len() {
			return nil, fmt.Errorf("array index out of bounds: %d", index)
		}
		element := rv.Index(index)
		return element.Interface(), nil
	default:
		return nil, fmt.Errorf("trying to access array index on non-array type: %T", data)
	}
}

// getObjectValue 获取对象属性值
func (pe *ParamExtractor) getObjectValue(data interface{}, key string) (interface{}, error) {
	// 处理不同类型的对象
	switch v := data.(type) {
	case map[string]interface{}:
		// JSON 解析后的 map
		if val, exists := v[key]; exists {
			return val, nil
		}
		return nil, fmt.Errorf("key '%s' not found in object", key)
	case map[interface{}]interface{}:
		// 其他 map 类型
		for k, val := range v {
			if kStr, ok := k.(string); ok && kStr == key {
				return val, nil
			}
		}
		return nil, fmt.Errorf("key '%s' not found in object", key)
	default:
		// 使用反射处理结构体
		rv := reflect.ValueOf(data)
		if rv.Kind() == reflect.Ptr {
			rv = rv.Elem()
		}
		if rv.Kind() == reflect.Struct {
			field := rv.FieldByName(key)
			if field.IsValid() && field.CanInterface() {
				return field.Interface(), nil
			}
			return nil, fmt.Errorf("field '%s' not found in struct", key)
		}
		return nil, fmt.Errorf("cannot access property '%s' on type %T", key, data)
	}
}

// HasPath 检查路径是否存在
func (pe *ParamExtractor) HasPath(path string) bool {
	_, err := pe.Extract(path)
	return err == nil
}
